/*
 * @Author: dadadaXU 1413107032@qq.com
 * @Date: 2024-10-16 22:11:53
 * @LastEditors: dadadaXU 1413107032@qq.com
 * @LastEditTime: 2025-01-05 22:02:20
 * @FilePath: /FileBackup/src/filePacker.cpp
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#include "../include/filePacker.hpp"

FilePacker::FilePacker(std::filesystem::path source_file_path, std::filesystem::path target_file_path)
    : sourceFilePath(std::filesystem::absolute(source_file_path).lexically_normal()),
      targetFilePath(std::filesystem::absolute(target_file_path).lexically_normal()),
      originalWorkPath(std::filesystem::current_path())
{
    if (!std::filesystem::exists(source_file_path))
    {
        std::cerr << "Invalid path: " << source_file_path << std::endl;
        return;
    }
}

FilePacker::~FilePacker()
{
    /* 还原当前工作目录 */
    if (std::filesystem::exists(originalWorkPath)) 
        std::filesystem::current_path(originalWorkPath);
}

void FilePacker::PreorderEnumerate(FileBaseUnit &package_out, std::filesystem::path path, int level)
{
    if (path.empty() || level > MAX_DIR_DEPTH)
        return;

    FileBaseUnit packing_file_in(path);
    if (!packing_file_in.Filter(fileFilterArguments)) // 过滤文件
        return;

    // std::cout << packing_file_in.filePath << " -> " << (int)(packing_file_in.GetFileType()) << std::endl;

    /* 保存文件 */
    switch (packing_file_in.GetFileType())
    {
    case FILE_TYPE_NORMAL:
        package_out.WriteFileHeader(packing_file_in.fileHeader);
        package_out.WriteDataBlock(packing_file_in);
        break;

    case FILE_TYPE_DIRECTORY:
        package_out.WriteFileHeader(packing_file_in.fileHeader);
        /* 对目录进行递归 */
        for (auto const &dir_entry : std::filesystem::directory_iterator(path))
        {
            // std::cout << " -> " << dir_entry << std::endl;
            PreorderEnumerate(package_out, dir_entry, level + 1);
        }
        break;

    case FILE_TYPE_PIPE:
        package_out.WriteFileHeader(packing_file_in.fileHeader);
        break;

    case FILE_TYPE_SYMBOLIC_LINK:
        package_out.WriteFileHeader(packing_file_in.fileHeader);
        package_out.WriteSymbolicLink(packing_file_in.filePath);
        break;

    case FILE_TYPE_HARD_LINK:
        if (inodeTable.count(packing_file_in.fileHeader.metadata.st_ino) > 0)
        {
            /* 保存链接路径 */
            package_out.WriteFileHeader(packing_file_in.fileHeader);
            package_out.WriteHardLink(packing_file_in.filePath);
        }
        else // 文件数据尚未打包 第一个硬链接
        {
            inodeTable[packing_file_in.fileHeader.metadata.st_ino] = packing_file_in.filePath;
            /* 当作普通文件保存 */
            packing_file_in.fileHeader.metadata.st_nlink = 1;
            package_out.WriteFileHeader(packing_file_in.fileHeader);
            package_out.WriteDataBlock(packing_file_in);
        }
        break;

    default:
        std::cout << packing_file_in.filePath << " XX " << std::endl;
        break;
    }
}

void FilePacker::PackFile(const FileFilterArguments filter_arguments, const std::string message)
{
    this->fileFilterArguments = filter_arguments;

    /* 修改当前工作目录 */
    std::filesystem::current_path(sourceFilePath.parent_path());

    /* 打包文件路径 */
    if (!std::filesystem::exists(targetFilePath.parent_path()))
        std::filesystem::create_directories(targetFilePath.parent_path());
    if (std::filesystem::is_directory(targetFilePath)) // 路径为目录则默认文件名
        targetFilePath.append(DEFAULT_PACK_FILENAME);
    if (targetFilePath.extension() != FILE_SUFFIX_PACK)
        targetFilePath.replace_extension(FILE_SUFFIX_PACK); // 添加后缀名

    /* 创建打包文件头 */
    BackupHeader backup_header = {0};
    std::strncpy(backup_header.filePath, sourceFilePath.c_str(), TAR_FILE_PATH); // 文件原始路径
    std::strncpy(backup_header.message, message.c_str(), PAK_MESSAGE_LEN);
    FileBaseUnit packageFile_out(targetFilePath, backup_header);

    /* 文件数据打包 */
    PreorderEnumerate(packageFile_out, sourceFilePath);

    /* 文件校验码 */
    packageFile_out.WriteCRC(packageFile_out.fileHeader.CRC, std::ios::beg);
}

bool FilePacker::UnpackFile(bool restore_metadata, bool go_back)
{
    if (sourceFilePath.extension() != FILE_SUFFIX_PACK)
    {
        std::cerr << "Invalid file: " << sourceFilePath << std::endl;
        return false;
    }

    /* 读取打包备份文件头 */
    FileBaseUnit restoreFile_in(sourceFilePath);
    BackupHeader backup_header = {0};
    restoreFile_in.ReadBackupHeader(backup_header);

    /* 切换工作路径 */
    if (go_back) // 文件原始路径
    {
        targetFilePath = std::filesystem::path(backup_header.filePath).parent_path();
        if (!std::filesystem::exists(targetFilePath))
        {
            std::cerr << "Reload file path failed: " << targetFilePath << std::endl;
            return false;
        }
    }
    std::filesystem::create_directories(targetFilePath);
    std::filesystem::current_path(targetFilePath);

    FileHeader fileHeader = {0};
    while (restoreFile_in.peek() != EOF)
    {
        /* 文件指针移动检查 */
        if (!restoreFile_in.CheckFileOffset(TAR_RECORD_SIZE))
        {
            std::cerr << "File read error: offset = " << restoreFile_in.tellg() << std::endl;
            return false;
        }

        /* 还原文件头 */
        if (!restoreFile_in.ReadFileHeader(fileHeader)) // 保证文件头有效
        {
            std::cerr << "Invalid FileHeader - " << fileHeader.filePath << std::endl;
            return false;
        }
        FileBaseUnit restoring_file_out(fileHeader);

        std::filesystem::path parent_path(restoring_file_out.filePath.parent_path());
        switch (restoring_file_out.GetFileType())
        {
        case FILE_TYPE_NORMAL:
            /* 还原目录 */
            if ((!std::filesystem::exists(parent_path)) && (!parent_path.empty()))
                std::filesystem::create_directories(parent_path);
            /* 还原数据（不打断整个解压过程）*/
            restoring_file_out.RestoreNormalFileData(restoreFile_in);
            // if (!restoring_file_out.RestoreNormalFileData(restoreFile_in))
            //     return false;
            break;

        case FILE_TYPE_DIRECTORY:
            std::filesystem::create_directories(fileHeader.filePath);
            break;

        case FILE_TYPE_PIPE:
            std::filesystem::remove_all(fileHeader.filePath);
            ::mkfifo(fileHeader.filePath, fileHeader.metadata.st_mode);
            break;

        case FILE_TYPE_SYMBOLIC_LINK:
            std::filesystem::remove_all(fileHeader.filePath);
            std::filesystem::create_symlink(
                restoreFile_in.ReadFileLink(),
                restoring_file_out.filePath);
            break;

        case FILE_TYPE_HARD_LINK:
            std::filesystem::remove_all(fileHeader.filePath);
            std::filesystem::create_hard_link(
                restoreFile_in.ReadFileLink(),
                restoring_file_out.filePath);
            break;

        default:
            break;
        }

        if (restore_metadata)
            restoring_file_out.RestoreMetadata();
    }

    /* 文件校验码 */
    if (backup_header.CRC != restoreFile_in.fileHeader.CRC)
    {
        std::cerr << "Restore file data failed! " << std::endl
                  << "\tOriginal CRC: " << backup_header.CRC << std::endl
                  << "\tRestore CRC:  " << restoreFile_in.fileHeader.CRC << std::endl;
        return false;
    }

    return true;
}